﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

/*
 * addinname
 *  MAterial name
 *  gamedll materials
 *
*/

namespace Scenario_Editor.Addin
{
    public class MaterialItem
    {
        #region Public Properties

        [XmlIgnore]
        public Engine.Graphics.IMaterial Material { get; set; }

        [XmlAttribute]
        public string MaterialName { get; set; }

        #endregion Public Properties
    }

    public class MaterialStorage
    {
        /// <summary>
        /// key = addinName,
        /// </summary>
        //private Dictionary<string, List<MaterialItem>> _materialsAddin;

        #region Internal Fields

        internal List<MaterialsForAddin> _materialsAddin;

        #endregion Internal Fields

        #region Public Constructors

        public MaterialStorage()
        {
            this._materialsAddin = new List<MaterialsForAddin>();// new Dictionary<string, List<MaterialItem>>();
        }

        #endregion Public Constructors

        #region Public Methods

        public static MaterialStorage FromFile(string filename, Engine.AddinManager.AddinManager addinManager,
            Engine.Graphics.MaterialManager materialManager)
        {
            MaterialStorage materialStorage = new MaterialStorage();
            StringBuilder errorMessage = new StringBuilder();

            using (XmlTextReader reader = new XmlTextReader(filename))
            {
                while (reader.Read())
                {
                    if (reader.NodeType == XmlNodeType.Element)
                    {
                        switch (reader.Name)
                        {
                            case "MaterialContainer":
                                string addinName = reader.GetAttribute("GameDll");
                                try
                                {
                                    materialStorage._materialsAddin.Add(MaterialsForAddin.FromXml(reader, addinName, addinManager, materialManager));
                                }
                                catch (Exception ex)
                                {
                                    errorMessage.AppendLine("Materials for " + addinName + " could not be loaded.\n\r"
                                        + ex.Message);
                                }
                                break;

                            default: Console.WriteLine("unknown Element: " + reader.Name); break;
                        }
                    }
                }
            }

            return materialStorage;
        }

        public void AddMaterial(string addinName, string materialName, Engine.Graphics.IMaterial material)
        {
            bool addinFound = false;
            foreach (var addinMaterials in this._materialsAddin)
            {
                if (addinMaterials.Addinname == addinName)
                {
                    addinFound = true;
                    bool materialFound = false;
                    foreach (var item in addinMaterials.Materials)
                    {
                        if (item.MaterialName == materialName)
                        {
                            item.Material = material;
                            materialFound = true;
                            return;
                        }
                    }
                    // If no material found add new to the list
                    addinMaterials.Materials.Add(new MaterialItem()
                    {
                        MaterialName = materialName,
                        Material = material
                    });

                    return;
                }
            }
            // When no material for the addin is found, we add new lists
            this._materialsAddin.Add(new MaterialsForAddin()
            {
                Addinname = addinName,
                Materials = new List<MaterialItem>()
                    {
                         new MaterialItem()
                         {
                              Material = material,
                               MaterialName = materialName
                         }
                    }
            });
        }

        public void ChangeOrAddMaterial(string addinName, string materialName, Engine.Graphics.IMaterial material)
        {
            foreach (var addin in this._materialsAddin)
            {
                if (addin.Addinname == addinName)
                {
                    foreach (var m in addin.Materials)
                    {
                        if (m.MaterialName == materialName)
                        {
                            m.Material = material;
                            return;
                        }
                    }
                    // When no material is found, the materials is add to list.
                    addin.Materials.Add(new MaterialItem()
                    {
                        MaterialName = materialName,
                        Material = material
                    });
                    return;
                }
            }
            this._materialsAddin.Add(new MaterialsForAddin()
            {
                Addinname = addinName,
                Materials = new List<MaterialItem>()
                {
                     new MaterialItem()
                    {
                        MaterialName = materialName,
                        Material = material
                    }
                }
            });
        }

        public Engine.Graphics.IMaterial GetMaterial(string addinName, string materialName)
        {
            bool materialsForAddinFound = false;
            foreach (var item in this._materialsAddin)
            {
                if (item.Addinname == addinName)
                {
                    materialsForAddinFound = true;
                    return item.GetMaterial(materialName);
                }
            }

            throw new Exception("No Materials for addin found: " + addinName);
        }

        public bool IsMaterialInStorage(string addinName, string materialName)
        {
            foreach (var item in this._materialsAddin)
            {
                if (item.Addinname == addinName)
                {
                    foreach (var material in item.Materials)
                    {
                        if (material.MaterialName == materialName)
                            return true;
                    }
                }
            }

            return false;
        }

        public void SaveToFile(string filename)
        {
            FileStream fs = new FileStream(filename, FileMode.Create);
            SaveToStream(fs);
            fs.Close();
        }

        public void SaveToStream(Stream stream)
        {
            try
            {
                using (TextWriter tw = new StreamWriter(stream, Encoding.Unicode))
                {
                    // Erst die Addins und für jedes Addin alle materialien.
                    using (XmlTextWriter writer = new XmlTextWriter(tw))
                    {
                        writer.Formatting = Formatting.Indented;
                        writer.Indentation = 4;

                        writer.WriteStartElement("MaterialStorage");

                        foreach (var item in this._materialsAddin)
                        {
                            writer.WriteComment("Addinname: " + item.Addinname.ToString());
                            writer.WriteStartElement("MaterialContainer");
                            writer.WriteAttributeString("GameDll", item.Addinname);

                            foreach (var material in item.Materials)
                            {
                                writer.WriteStartElement("MaterialItem");
                                writer.WriteAttributeString("Name", material.MaterialName);
                                material.Material.ToXml(writer);
                                writer.WriteEndElement();
                            }

                            writer.WriteEndElement();
                        }

                        writer.WriteEndElement(); // MateraialStorage
                    }
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        #endregion Public Methods
    }

    internal class MaterialsForAddin
    {
        #region Public Constructors

        public MaterialsForAddin()
        {
            this.Materials = new List<MaterialItem>();
        }

        #endregion Public Constructors

        #region Public Properties

        [XmlAttribute]
        public string Addinname { get; set; }

        public List<MaterialItem> Materials { get; set; }

        #endregion Public Properties

        #region Public Methods

        public static MaterialsForAddin FromXml(XmlTextReader reader, string addinName,
            Engine.AddinManager.AddinManager addinManager, Engine.Graphics.MaterialManager materialManager)
        {
            MaterialsForAddin material = new MaterialsForAddin();

            try
            {
                addinManager.SetAddin(addinName);
                var addin = addinManager.CurrentAddin;
                material.Addinname = addinName;

                while (reader.Read())
                {
                    if (reader.NodeType == XmlNodeType.Element)
                    {
                        if (reader.Name == "MaterialItem")
                        {
                            string materialName = reader.GetAttribute("Name");

                            if (materialName != string.Empty)
                                material.Materials.Add(new MaterialItem()
                                {
                                    MaterialName = materialName,
                                    Material = (Engine.Graphics.IMaterial)addin.LoadMaterialXml(reader, materialManager)
                                });

                            while (reader.Read())
                            {
                                // Read to end of MaterialItem
                                if (reader.NodeType == XmlNodeType.EndElement)
                                {
                                    if (reader.Name == "MaterialItem")
                                        break; ;
                                }
                            }
                        }
                    }
                    if (reader.NodeType == XmlNodeType.EndElement && reader.Name != "MaterialItem")
                        return material;
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }

            throw new Exception("End Of Material Item could not be found");
        }

        #endregion Public Methods

        #region Internal Methods

        internal Engine.Graphics.IMaterial GetMaterial(string materialName)
        {
            foreach (var item in this.Materials)
            {
                if (item.MaterialName == materialName)
                    return item.Material;
            }

            throw new Exception("Material not found: " + materialName
                + "\n\rAddinname: " + this.Addinname);
        }

        #endregion Internal Methods
    }
}